/* Copyright (c) 2007-2024 by Kithara Software GmbH. All rights reserved. */

//##############################################################################################################
//
// File:         NetworkUdpReceive.cpp
//
// Used Modules: Network Module
//
// Description:  Sample application for simple implementation of a UDP receiver
//
// Creator:      r.gro 2007-12-11
//
//##############################################################################################################

   /*=====================================================================*\
   |                    *** DISCLAIMER OF WARRANTY ***                     |
   |                                                                       |
   |       THIS  CODE AND INFORMATION IS PROVIDED "AS IS"  WITHOUT         |
   |       A  WARRANTY OF  ANY KIND, EITHER  EXPRESSED OR  IMPLIED,        |
   |       INCLUDING  BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF         |
   |       MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.        |
   \*=====================================================================*/

   //##############################################################################################################
   //
   // Purpose:
   //
   // This example shows how to receive UDP datagrams.
   //
   // First we open the network adapter and create a socket.
   // A receive callback is installed at kernel level, to put incoming data into a message pipe and set an event.
   // A receive thread of the application, waiting for that event, gets the data from the pipe and displays it.
   //
   // This sample is designed to be the counterpart to the sample NetworkUdpSend.
   //
   // PLEASE SPECIFY YOUR IP-ADDRESS, SUBNET-MASK AND GATEWAY-IP-ADDRESS IN THE KS_makeIPv4() CALLS AS WELL AS
   // THE PORT TO USE IN THE APPLICATION PROGRAM BEFORE RUNNING THIS EXAMPLE!
   //
   //##############################################################################################################


   //--------------------------------------------------------------------------------------------------------------
   // To share the definition of data structures between the main program and the kernel DLL, we use a common
   // header file.
   //--------------------------------------------------------------------------------------------------------------

#include "NetworkUdp.h"


//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
//
// Don't forget to enter your zero-leading, 6-digit customer number!
// This is required to open the driver.
//
// If you use a Demo Version, "DEMO" is also allowed instead.
// If you use a Beta Version, "BETA" is also allowed instead.
//
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

const char _pCustomerNumber[] = "DEMO";


//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// The _pApp pointer is global because it will be used by the main thread as well as by the data receiving
// thread.
// ATTENTION:
// Using the raw pointer is safe in this case, because both threads are guaranteed to be executed in user
// space and thus using the same bitsize for pointers.
// However: Passing raw pointers between kernel and user space should be avoided because the user space
// application and the kernel space DLL might run on different bitsizes (and thus pointer sizes).
// To circumvent this problem pass only handles (which are guaranteed to be 32 bit on 64 bit as well as 32 bit
// systems) between user space and kernel space and acquire a raw pointer to the memory referenced by the
// handle with KS_getSharedMemEx().
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

SharedData* _pApp = NULL;


//--------------------------------------------------------------------------------------------------------------
// Declaration of the function that will be excecuted by the receiving thread.
// Definition of the function is at the end of this file.
//--------------------------------------------------------------------------------------------------------------

KSError __stdcall _recvThread(void* /*pArgs*/);


//--------------------------------------------------------------------------------------------------------------
// This is the main program.
//--------------------------------------------------------------------------------------------------------------

void runSample() {
    outputTxt("***** Kithara example program 'NetworkUdp' *****");

    KSError ksError;


    //------------------------------------------------------------------------------------------------------------
    // Opening the driver is always the first step!
    // After that, we can use other functions. If the opening fails, no other function can be called.
    //
    // This function takes your customer number with Kithara (or "DEMO" or "BETA" if applicable) as a parameter.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_openDriver(
        _pCustomerNumber);                        // Customer number
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_openDriver", "Unable to open the driver!");
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // Allocate shared memory for communication between the DLL that gets loaded to realtime context and this
    // user space application.
    //------------------------------------------------------------------------------------------------------------

    KSHandle hSharedMemory;
    ksError = KS_createSharedMemEx(
        &hSharedMemory,                           // Address to write the new shared memory handle to
        "",                                       // Name of shared memory
        sizeof(SharedData),                       // Size of shared memory
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_createSharedMemEx", "Unable to create shared memory!");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // To access the shared memory the application needs to acquire a pointer to the allocated shared memory
    // using the handle of the just created shared memory.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_getSharedMemEx(
        hSharedMemory,                            // Handle of the shared memory
        (void**)&_pApp,                           // Address of pointer to SharedData structure
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_getSharedMemEx", "Unable to map shared memory!");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // Determine the system's bitsize to decide if either to load the 32 bit or 64 bit kernel DLL.
    //------------------------------------------------------------------------------------------------------------

    KSSystemInformation systemInfo;                       // KSSystemInformation instance
    systemInfo.structSize = sizeof(KSSystemInformation);  // Don't forget to init structSize!
    ksError = KS_getSystemInformation(
        &systemInfo,                              // Address of KSSystemInformation structure
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_getSystemInformation", "Unable to get system information to distinguish bitsize!");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // In order to use functions from the DLL on the kernel level, it must have been loaded first.
    //
    // Attention! The DLL must be found by the loader, so it should be placed in a directory, which is part
    // of the search path!
    //
    // Because we want to pass the handle of the loaded kernel in shared memory we don't use the
    // init function on loading the kernel but will call the init function explicitly later, after filling
    // the kernel handle and all information needed to initialize the kernel into the SharedData structure.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_loadKernel(
        &_pApp->hKernel,                          // Address to write kernel handle to
        systemInfo.isSys64Bit ?                   // Name of DLL
        "NetworkUdp_64.dll" :            // either the 64 bit or
        "NetworkUdp_32.dll",             // the 32 bit kernel
        NULL,                                     // Name of init function (not used)
        NULL,                                     // Init parameter (not used)
        KSF_KERNEL_EXEC);                         // Flags, here load to kernel address space
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_loadKernel", "Unable to load DLL! Is the DLL in the search path?");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // First, the names of all network adapters are queried and displayed.
    //------------------------------------------------------------------------------------------------------------

    char pDeviceName[256];

    outputTxt(" ");
    outputTxt("Following network adapters found:");

    for (int i = 0;; ++i) {


        //----------------------------------------------------------------------------------------------------------
        // KS_enumDevices() can be used to query the names of all network adapters assigned to the Kithara
        // Driver. The number 'i' runs starting at zero.
        //----------------------------------------------------------------------------------------------------------

        ksError = KS_enumDevices(
            "NET",                                  // 'NET' searches for network adapters
            i,                                      // Enumeration index, starting at zero
            pDeviceName,                            // Buffer for device name
            KSF_NO_FLAGS);                          // Flags, here none
        if (ksError != KS_OK) {
            if (KSERROR_CODE(ksError) != KSERROR_DEVICE_NOT_FOUND)
                outputErr(ksError, "KS_enumDevices", "Unable to query network device name!");
            if (KSERROR_CODE(ksError) == KSERROR_DEVICE_NOT_FOUND && !i) {
                outputTxt("No network adapters found");
                outputTxt(" ");
                KS_closeDriver();
                return;
            }
            break;
        }
        outputDec(i, "", ": ", false);
        outputTxt(pDeviceName);
    }
    outputTxt(" ");


    //------------------------------------------------------------------------------------------------------------
    // Please enter the index of the adapter, which should be opened.
    //------------------------------------------------------------------------------------------------------------

    outputTxt("Attention!");
    outputTxt("By selecting a device its Windows driver gets removed and replaced by the ");
    outputTxt("appropriate Kithara driver. This will render the network device for the duration");
    outputTxt("of this sample invisible to Windows.");
    outputTxt("Be sure that all other Applications using that device are closed right now!");

    int deviceIndex = inputDec("Device number: ", 0);
    ksError = KS_enumDevices(
        "NET",                                    // 'NET' searches for network devices
        deviceIndex,                              // Index of the chosen network device
        _pApp->pDeviceName,                       // Buffer for device name
        KSF_NO_FLAGS);                            // Flags, here none
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_enumDevices", "Unable to query selected network device name!");
        KS_closeDriver();
        return;
    }

    outputTxt("Selected device: ", false);
    outputTxt(_pApp->pDeviceName);


    //------------------------------------------------------------------------------------------------------------
    // For sending IP packets we must set our own IP address and the subnet mask.
    // Note: The data in Ethernet frames must be 'big endian'!
    // KS_htonl() converts an uint from host to net.
    // KS_ntohl() converts an uint from net to host.
    // KS_htons() converts an ushort from host to net.
    // KS_ntohs() converts an ushort from net to host.
    // KS_makeIPv4() builds an IP address.
    //------------------------------------------------------------------------------------------------------------

    int local_current_last_ip = inputDec("local current last ip ", 181);
    int remote_current_last_ip = inputDec("remote current last ip ", 182);
    int remote_port = inputDec("remote port ", 2120);

    KS_makeIPv4(&_pApp->ipConfig.localAddress, 192, 168, 0, local_current_last_ip);
    KS_makeIPv4(&_pApp->ipConfig.subnetMask, 255, 255, 255, 0);
    KS_makeIPv4(&_pApp->ipConfig.gatewayAddress, 192, 168, 0, 1);
    KS_makeIPv4(&_pApp->serverIp, 192, 168, 0, 182);
    _pApp->listeningPort = remote_port;

    outputTxt("Current local IP address:       ", false);
    outputIpv4(KS_ntohl(_pApp->ipConfig.localAddress));
    outputTxt("Current subnet mask:            ", false);
    outputIpv4(KS_ntohl(_pApp->ipConfig.subnetMask));
    outputTxt("Current gateway address:        ", false);
    outputIpv4(KS_ntohl(_pApp->ipConfig.gatewayAddress));
    outputTxt("Using port:                     ", false);
    outputDec(_pApp->listeningPort);
    outputTxt("Otherwise change the sample!");
    outputTxt(" ");


    //------------------------------------------------------------------------------------------------------------
    // Now call the init function of the kernel and pass the handle of the shared memory, so the kernel
    // can retrieve the pointer to the shared memory from the handle and do all necessary resource
    // allocations based on the information stored in the shared memory.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_execKernelFunctionEx(
        _pApp->hKernel,                           // Kernel handle
        "_initFunction",                          // Name of function
        hSharedMemory,                            // Handle of the shared memory
        KS_INVALID_HANDLE,                        // Context (unused)
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_execKernelFunctionEx", "Unable to initialize the kernel DLL!");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // Create (and start) the thread that will receive the data in userspace, forwarded through the message pipe
    // from the receiving callback in the kernel space DLL.
    //------------------------------------------------------------------------------------------------------------

    _pApp->isFinished = 0;

    ksError = KS_createThread(
        _recvThread,                              // Thread function
        NULL,                                     // Reference parameter to the thread (unused)
        NULL);                                    // Address to write thread handle to (unused)
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_createThread", "Unable to create the receiving thread!");
        KS_closeDriver();
        return;
    }

    //------------------------------------------------------------------------------------------------------------
    // Now, text messages can be entered and sent to the receiver.
    // Enter 'Q' to quit the sample.
    //------------------------------------------------------------------------------------------------------------

    outputTxt("Enter <q> to quit");
    for (;;) {
        const char* pTxt = inputTxt();
        if ((pTxt[0] == 'q' || pTxt[0] == 'Q') && pTxt[1] == '\0')
            break;

        strcpy(_pApp->pSendBuffer, pTxt);
        _pApp->sendLength = (int)strlen(pTxt);
        ksError = KS_execKernelFunctionEx(
            _pApp->hKernel,                          // Kernel handle
            "_sendBufferContent",                   // Name of function
            KS_INVALID_HANDLE,                      // Parameter for function call (unused)
            KS_INVALID_HANDLE,                      // Context (unused)
            KSF_NO_FLAGS);                          // Flags (unused)
        if (ksError != KS_OK) {
            outputErr(ksError, "_sendBufferContent", "Error while sending!");
            break;
        }
    }
    outputTxt(" ");


    //------------------------------------------------------------------------------------------------------------
    // Signal the receiving thread to end receiving by setting the SharedData.isFinished flag and
    // trigger the thread to evaluate the flag by emitting the SharedData.hReceiveEvent event.
    //------------------------------------------------------------------------------------------------------------

    outputTxt("Quitting...");
    _pApp->isFinished = 1;

    ksError = KS_setEvent(
        _pApp->hReceiveEvent);                    // Event handle
    if (ksError != KS_OK)
        outputErr(ksError, "KS_setEvent", "Setting event failed!");


    //------------------------------------------------------------------------------------------------------------
    // Now let the DLL clean up resources it did allocate.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_execKernelFunctionEx(
        _pApp->hKernel,                           // Kernel handle
        "_exitFunction",                          // Name of function
        KS_INVALID_HANDLE,                        //  Parameter for function call (unused)
        KS_INVALID_HANDLE,                        // Context (unused)
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK) {
        outputErr(ksError, "KS_execKernelFunctionEx", "Error while deallocating resources on kernel level!");
        KS_closeDriver();
        return;
    }


    //------------------------------------------------------------------------------------------------------------
    // Unload the kernel DLL using the shared handle.
    // Although KS_closeDriver() frees all allocated resources (like shared memory and loaded kernels),
    // it is good style to explicitly free ahead resources that you did allocate.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_freeKernel(
        _pApp->hKernel);                          // Kernel handle
    if (ksError != KS_OK)
        outputErr(ksError, "KS_freeKernel", "Unable to unload the kernel!");


    //------------------------------------------------------------------------------------------------------------
    // Free the shared memory using its local stored handle.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_freeSharedMemEx(
        hSharedMemory,                            // Handle of shared memory
        KSF_NO_FLAGS);                            // Flags (unused)
    if (ksError != KS_OK)
        outputErr(ksError, "KS_freeSharedMemEx", "Unable to remove shared memory!");


    //------------------------------------------------------------------------------------------------------------
    // At last we have to close the driver to free any allocated resources.
    //------------------------------------------------------------------------------------------------------------

    ksError = KS_closeDriver();
    if (ksError != KS_OK)
        outputErr(ksError, "KS_closeDriver", "Unable to close the driver!");

    waitTime(500 * ms);
    outputTxt(" ");
    outputTxt("End of program 'NetworkUdpReceive'.");
}


//--------------------------------------------------------------------------------------------------------------
// Function that will be excecuted by the receiving thread.
// It contains a loop in which the thread waits for the event signalization.
// When the event is signalled it reads one message of up to %bufferSize% bytes from the message pipe
// The read data will be terminated with a '\0' for getting displayed and output to the console.
//--------------------------------------------------------------------------------------------------------------

KSError __stdcall _recvThread(void* /*pArgs*/) {

    KSError ksError = KS_OK;
    char pReadBuffer[bufferSize];
    int length;

    while (_pApp->isFinished == 0) {


        //----------------------------------------------------------------------------------------------------------
        // Wait for the event to be set.
        //----------------------------------------------------------------------------------------------------------

        ksError = KS_waitForEvent(
            _pApp->hReceiveEvent,                   // Event handle
            KSF_NO_FLAGS,                           // Flags (unused)
            0);                                     // Time-out, in 100-ns-units. 0 = infinite
        if (ksError != KS_OK)
            return ksError;


        //----------------------------------------------------------------------------------------------------------
        // Take incoming data from the message pipe and print it out.
        //----------------------------------------------------------------------------------------------------------

        while (KS_OK == KS_getPipe(
            _pApp->hPipe,                     // Handle of the pipe
            pReadBuffer,                      // Buffer to write the message, stored in pipe, to
            bufferSize,                       // Maximum size of the buffer in bytes
            &length,                          // Address to write the number of gotten bytes to
            KSF_NO_FLAGS)) {                  // Flags, here none
            pReadBuffer[length] = '\0';                       // Delimit the received string for output
            outputTxt(pReadBuffer, false);
        }
    }

    outputTxt("Receiving thread has been finished.");
    return ksError;
}
